Slackワークフローの定期実行をGASのタイマー経由に切り替えてみた
Slackワークフローのタイマー起動は、スケジュール変更などの例外が発生すると手作業での停止に追われることになります。
毎週金曜に動作するワークフローをスケジュールの都合で木曜に変更する必要が生じました。その対策を考えてカレンダーを見ていた際に年末年始も思い出し、一時的な解決策では不十分かもしれないと気づきました。そこで、ワークフロースケジュール管理の根本的な見直しに着手しました。
ワークフローの見直し
当初のワークフローを簡潔に図解すると、以下のようになります。
Slackワークフローのタイマーは、事前に設定した構成以外での動作ができません。GASのタイマーも同様に事前設定が必要ですが、コード上で動作を変更できる利点があります。そこで、Slack側の仕組みをwebhookに切り替え、GASから呼び出す方式に変更しました。
Slackワークフローについては起動指定の変更のみとなりますが、GASについては一からの作成となります。
タイマー設定シートの要件
最低限必要な要素としては以下の通り。
- 通知あるいはスキップの指定
- 該当日付
この仕組みは金曜日に動作することが前提です。つまり、通知指定で入力するのは金曜日以外の場合であり、スキップ指定は金曜日に対して行います。
問題は、入力した日付の曜日が誤っていた場合です。対策として、日付をもとに曜日を自動的に補完表示させました。この処理はマクロのみで実現可能でした。
曜日の判定基準を決めるのには苦心しました。検討した案は二つありました。一つは、入力した日付に通知させる代わりに、その週の金曜日をスキップさせる方法。もう一つは、入力した日付ごとに通知かスキップかを決める方法です。
今回は後者を採用しました。日付ごとにスキップか通知かを個別に指定する方法なら、入力規則を知らなくても一目で理解できるはずです。
タイマー設定シート作成
シート構成は以下の通りです。読み取るデータに入力ミスが発生しないよう、入力規則にて強制しています。
GASは以下の通り。
const sheetName = "シート1"
const colIdx = 1.0
const status_index = 3.0
const FRIDAY = 5.0
// 日付から設定を自動補完する用途
function onEdit(e) {
const sheet = e.source.getActiveSheet()
const range = e.range
const rule = sheet.getRange(2,3,999).getDataValidation();
if(sheet.getName() !== sheetName) return
if(range.getColumn() != colIdx) return
if (rule != null) {
var status_cell = sheet.getRange(range.getRowIndex(), status_index)
status_cell.setBackground('#cccccc')
if (range.getValues()[0][0].length == 0) {
status_cell.setValue(null)
} else {
var args = rule.getCriteriaValues();
var listValues = args[0];
// 金曜日が入力された場合はスキップ想定
if (listValues.length > 0 && range.getValues()[0][0] != null && range.getValues()[0][0].getDay() == FRIDAY) {
status_cell.setValue(listValues[0])
}
}
status_cell.setBackground('#ffffff')
}
}
解説向けに用意したシートには以下の書き置きをしています。
- 規定の通知設定は金曜
- 通知設定が「通知」となっている場合は、指定日にも通知する
- 通知設定が「スキップ」となっている場合は、指定日に通知しない
- 指定日が金曜の場合はスキップする前提として設定を自動補完する
- 同日付が複数存在する場合、下の方に書かれている設定が優先
なお、データ入力規則についてはGASコードとして落とし込んでいません。セルのスタイル設定がまだGAS側で対応していないので、設定したスタイルが消去されるからです。
Slackワークフロー動作用読み取り
上記シートのデータを定期的に読み取り、条件が一致した場合にSlackワークフローを起動させるGASも設定します。
var prop = PropertiesService.getScriptProperties();
const slack_notice_webhook_develop = prop.getProperty('SLACK_NOTICE_WEBHOOK_DEVELOP')
const slack_notice_webhook_product = prop.getProperty('SLACK_NOTICE_WEBHOOK_PRODUCT')
const dev_mode = false
// 週末通知設定のIDとシート名を指定
const SPREADSHEET_ID = '';
const SHEET_NAME = 'シート1';
const FRIDAY = 5;
function checkNotificationable(today = new Date()) {
const hour = today.getHours();
// 毎日15時にのみ処理を進める
if (hour !== 15) return false;
// 週末通知設定からデータ取得
const sheet = SpreadsheetApp.openById(SPREADSHEET_ID).getSheetByName(SHEET_NAME);
const data = sheet.getDataRange().getValues();
let shouldNotify = false;
// 金曜は通知規定日
const dayOfWeek = today.getDay();
if (dayOfWeek === FRIDAY) {
shouldNotify = true
}
const formattedDate = Utilities.formatDate(today, Session.getScriptTimeZone(), 'yyyy/MM/dd');
for (let i = 1; i < data.length; i++) {
const rowDate = data[i][0]; // 日付列
const notificationSetting = data[i][2]; // 通知設定列
if (Utilities.formatDate(new Date(rowDate), Session.getScriptTimeZone(), 'yyyy/MM/dd') === formattedDate) {
switch (notificationSetting) {
case '通知': // 今日の通知設定が「通知」の場合は通知を強制的に実行
shouldNotify = true;
break;
case 'スキップ': // 今日の通知設定が「スキップ」の場合は通知をスキップ
shouldNotify = false;
break;
}
}
}
return shouldNotify;
}
function pickSlackWebhook() {
return dev_mode ? slack_notice_webhook_develop : slack_notice_webhook_product;
}
function postSlack() {
const options = {
"method": 'POST',
"headers": {
'Content-Type': 'application/json'
},
"muteHttpExceptions": true
};
UrlFetchApp.fetch(pickSlackWebhook(), options);
}
function main() {
if (!checkNotificationable()) return;
postSlack()
}
あとがき
今回の対応で、ワークフローの定時起動設定を細かく弄る必要がなくなり、且つ先の日程で通知及びスキップの設定も予定が組めるようになりました。
何気に一番手間がかかったのはデータ入力規則の読み取りです。スタイル設定が取れなかったため、取得したデータ構成に何が含まれているのかの確認に時間がかかっていました。
例外スケジュールが難点のSlackワークフローですが、GASと組み合わせることで柔軟に対応することが可能です。年末年始での動作に不安がある場合は検討をおすすめします。